package org.itsolutions.javaBackup.buffers;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.itsolutions.javaBackup.exceptions.AException;
import org.itsolutions.javaBackup.logger.ELogLevel;
import org.itsolutions.javaBackup.logger.ILogger;
import org.itsolutions.javaBackup.logger.LoggerHelper;
import org.itsolutions.javaBackup.properties.GlobalProperty;
import org.itsolutions.javaBackup.spi.IServiceWithLifecycle;

import com.google.inject.Inject;

/**
 * 
 * @author Tomas Fecko
 */
public final class ByteBufferPool implements IServiceWithLifecycle {
	
	private static final ILogger					LOGGER = LoggerHelper.getLogger(ByteBufferPool.class);
	private static final int						NUMBER_OF_BUFFERS = GlobalProperty.NUMBER_OF_BUFFERS.get();
	private static final int						SIZE_OF_BUFFER = GlobalProperty.SIZE_OF_ONE_BYTE_BUFFER.get();
	private final AtomicBoolean						mIsStarted = new AtomicBoolean(false);
	private final BlockingQueue<ByteBufferWrapper> 	mQueue = new LinkedBlockingQueue<ByteBufferWrapper>(NUMBER_OF_BUFFERS);

	/**
	 * 
	 */
	@Inject public ByteBufferPool() {
	}
	
	@Override
	public void startService() throws AException {
		if (GlobalProperty.USE_BYTE_BUFFER_POOLS.get()) {
			LOGGER.log(ELogLevel.DEBUG, "Starting ByteBufferPool service.");
			long start = System.nanoTime();
			for (int x = 0; x < NUMBER_OF_BUFFERS; x++) {
				mQueue.add(new ByteBufferWrapper(ByteBuffer.allocateDirect(SIZE_OF_BUFFER), this, true));
			}
			long end = System.nanoTime();
			LOGGER.log(ELogLevel.DEBUG, "Creation of ", NUMBER_OF_BUFFERS, " byteBuffers of size:", SIZE_OF_BUFFER, " bytes took: ", 
					NANOSECONDS.toMillis(end - start), " millis.");
			mIsStarted.set(true);
		} else {
			LOGGER.log(ELogLevel.DEBUG, "NotStarting ByteBufferPool service, as config value is false.");			
		}
	}
	
	@Override
	public void stopService() throws AException {
		LOGGER.log(ELogLevel.DEBUG, "Stopping ByteBufferPool service.");
		ByteBufferWrapper wrapper = mQueue.poll();
		while (wrapper != null) {
			wrapper = null;
			wrapper = mQueue.poll();
		}
		mQueue.clear();
		mIsStarted.set(false);
	}
	@Override
	public boolean isStarted() {
		return mIsStarted.get();
	}
	
	/**
	 * @return the sizeOfBuffer
	 */
	public int getSizeOfBuffer() {
		return SIZE_OF_BUFFER;
	}
	
	/**
	 * 
	 * @return number of buffers in queue
	 */
	public int getAvailablePooledBuffers() {
		return mQueue.size();
	}
	
	/**
	 * 
	 * @return number of buffers that was lended from pool
	 */
	public int getUsedBuffersCount() {
		return mQueue.remainingCapacity();
	}

	/**
	 * 
	 * @return byte buffer wrapper
	 */
	public ByteBufferWrapper pollFreeBuffer() {
		return mQueue.poll();
	}
	
	/**
	 * 
	 * @param pTimeOut timeout
	 * @param pTimeUnit unit
	 * @return byteBuffer wrapper
	 */
	public ByteBufferWrapper pollFreeBuffer(long pTimeOut, TimeUnit pTimeUnit) {
		try {
			return mQueue.poll(pTimeOut, pTimeUnit);
		} catch (InterruptedException e) {
			LOGGER.log(ELogLevel.ERROR, e);
		}
		return null;
	}
	
	/**
	 * 
	 * @param pByteBuffer returned byted buffer
	 * @return true if success
	 */
	public boolean returnByteBuffer(ByteBufferWrapper pByteBuffer) {
		if (pByteBuffer == null || !pByteBuffer.isFromPool()) {
			return false;
		}
		pByteBuffer.clear();
		return mQueue.offer(pByteBuffer);
	}
}







